package com.jacklin.snowy.limitstrategy.apsect;

import com.jacklin.snowy.limitstrategy.annotation.RequestLimiter;
import com.jacklin.snowy.limitstrategy.dto.RequestLimitDTO;
import com.jacklin.snowy.limitstrategy.factory.RequestLimitFactory;
import com.jacklin.snowy.limitstrategy.service.RequestLimitService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 限流切面实现
 *
 * @author: jacklin
 * @since: 2022/5/10 14:44
 */
@Slf4j
@Aspect
@Component
public class RequestLimitAspect {

    @Autowired
    private RequestLimitFactory factory;


    /**
     * 切入点
     */
    @Pointcut(value = "@annotation(com.jacklin.snowy.limitstrategy.annotation.RequestLimiter)")
    public void requestLimit() {
        // 切入点方法
    }

    /**
     * 前置切点
     *
     * @param joinPoint 切入点
     */
    @Before("requestLimit()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("-------------------------------doBefore begin------------------------------------");

        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        RequestLimiter limiter = targetMethod.getAnnotation(RequestLimiter.class);
        log.info("限流方式：【{}】", limiter.type().getDesc());
        RequestLimitService service = factory.build(limiter.type());
        if (service == null) {
            log.info("【{}】无对应限流操作类型，直接放行", limiter.type());
        } else {
            RequestLimitDTO dto = new RequestLimitDTO();
            dto.setLimiter(limiter);
            dto.setKey(signature.getName());
            if (service.checkRequestLimit(dto)) {
                throw new RuntimeException("【" + limiter.type().getDesc() + "】限流控制");
            }
        }

        log.info("-------------------------------doBefore end------------------------------------");
    }

    /**
     * 环绕切点
     *
     * @param proceedingJoinPoint 切入点
     * @return 方法执行返回
     */
    @Around("requestLimit()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) {
        try {
            return proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            return e.getMessage();
        }
    }

    /**
     * 后置切入点
     *
     * @param joinPoint 切入点
     * @param obj       返回值
     */
    @AfterReturning(pointcut = "requestLimit()", returning = "obj")
    public void doAfterReturning(JoinPoint joinPoint, Object obj) {
    }

}
